﻿using Force.DeepCloner;
using HalconDotNet;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml.Serialization;
using VisionRoc.Core;
using VisionRoc.Tool;

namespace VisionRoc_Measure
{
    public partial class FormCalibrationTool : Form
    {
        public static FormCalibrationTool FormSingle = null;

        public static FormCalibrationTool GetSingle()
        {
            if (FormSingle == null)
            {
                FormSingle = new FormCalibrationTool( );
            }
            return FormSingle;
        }

      
        public FormCalibrationTool()
        {
            this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);//减少闪烁
            this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);//双缓冲
            this.SetStyle(ControlStyles.ResizeRedraw, true);//调整大小时进行重绘
            this.SetStyle(ControlStyles.Selectable, true);//接收焦点
            this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
            this.SetStyle(ControlStyles.UserPaint, true);
            this.DoubleBuffered = true;//避免时间显示控件闪烁
            SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
            UpdateStyles();
            InitializeComponent();
        }

        List<PointPair> PointPairs = new List<PointPair>();

        CalibrationData CalibrationData;

        public string TimeNow { get => DateTime.Now.ToString("yyyy:MM:dd-hh:mm:ss") + Environment.NewLine; }

        private void FormDownCameraCalibration_Load(object sender, EventArgs e)
        {
            IniDataGridView(dataGridView1);
            //默认是仿射变换
            this.Combox_CalibrationType.DataSource = new string[] { "仿射变换", "旋转标定" ,"平移旋转标定"};
            this.Combox_CalibrationType.SelectedIndex = 0;
            this.Combox_TranslationTimes.DataSource = new string[] { "3", "4", "5", "6", "7", "8", "9" };
            this.Combox_TranslationTimes.SelectedIndex = 0;
            this.Combox_RotateTimes.DataSource = new string[] { "0" };
            this.Combox_RotateTimes.SelectedIndex = 0;

            DataGridViewTextBoxColumn TextBoxColumnPointType = dataGridView1.Columns[0] as DataGridViewTextBoxColumn;
            TextBoxColumnPointType.ReadOnly = true;

        }
      

        /// <summary>
        /// 初始化 DataGridView
        /// </summary>
        /// <param name="dataGridView"></param>
        public void IniDataGridView(DataGridView dataGridView)
        {

            //自动增加列
            dataGridView.AutoGenerateColumns = false;
            //行高根据内容自适应
            dataGridView.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.AllCellsExceptHeaders;
            //不显示增加行功能
            dataGridView.AllowUserToAddRows = false;
            dataGridView.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
            //不允许多选
            dataGridView.MultiSelect = false;

            //标题显示居中
            dataGridView.ColumnHeadersDefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
            //内容显示居中
            dataGridView.RowsDefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
            // 禁拖动行高度
            dataGridView.AllowUserToResizeRows = true;
            //字体表格设置
            dataGridView.DefaultCellStyle.Font = new Font("微软雅黑", 12, FontStyle.Bold);
            //字体表格设置
            dataGridView.ColumnHeadersDefaultCellStyle.Font = new Font("微软雅黑", 11, FontStyle.Bold);
            //设置横向滚动条
            dataGridView.ScrollBars = ScrollBars.Both;
            //隔行颜色
            dataGridView1.RowsDefaultCellStyle.BackColor = Color.White;
            dataGridView1.AlternatingRowsDefaultCellStyle.BackColor = Color.WhiteSmoke;
            //header颜色设置才生效,否则无效
            dataGridView1.EnableHeadersVisualStyles = false;
            dataGridView1.ColumnHeadersDefaultCellStyle.BackColor = Color.FromArgb(185, 209, 234);
            dataGridView1.ColumnHeadersDefaultCellStyle.ForeColor = Color.Black;
            dataGridView1.ColumnHeadersDefaultCellStyle.SelectionForeColor = Color.White;
        }

        private void dataGridView1_DataError(object sender, DataGridViewDataErrorEventArgs e)
        {
            MessageBox.Show("请输入正确的数字");
        }

        private void dataGridView1_RowStateChanged(object sender, DataGridViewRowStateChangedEventArgs e)
        {
            e.Row.HeaderCell.Value = (e.Row.Index + 1).ToString().PadLeft(2, '0');//添加行号前面补0
        }

        private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                PointPairs.Clear();

                if (this.Combox_TranslationTimes.Text != "0")
                {
                    int Translation = Convert.ToInt32(this.Combox_TranslationTimes.Text);
                    for (int i = 0; i < Translation; i++)
                    {
                        PointPairs.Add(new PointPair("平移"));
                    }
                }

                if (this.Combox_RotateTimes.Text != "0")
                {
                    int Rotate = Convert.ToInt32(this.Combox_RotateTimes.Text);
                    for (int i = 0; i < Rotate; i++)
                    {
                        PointPairs.Add(new PointPair("旋转"));
                    }
                }

                BindingList<PointPair> bs = new BindingList<PointPair>(PointPairs);
                dataGridView1.DataSource = bs;
                dataGridView1.Refresh();

                CalibrationData = null;
                this.TextBox_Log.Text = $"{TimeNow} 应用标定数据表格成功" + Environment.NewLine; ;
            }
            catch
            {
                this.TextBox_Log.Text = $"{TimeNow} 应用标定数据表格失败!!!" + Environment.NewLine; ;
            }

        }

        private void Combox_CalibrationType_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (this.Combox_CalibrationType.SelectedIndex == 0)
            {
                this.Combox_TranslationTimes.DataSource = new string[] { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
                this.Combox_TranslationTimes.SelectedIndex = 0;
                this.Combox_RotateTimes.DataSource = new string[] { "0" };
                this.Combox_RotateTimes.SelectedIndex = 0;
            }
            if (this.Combox_CalibrationType.SelectedIndex == 1)
            {
                this.Combox_TranslationTimes.DataSource = new string[] { "0" };
                this.Combox_TranslationTimes.SelectedIndex = 0;
                this.Combox_RotateTimes.DataSource = new string[] { "3", "4", "5" };
                this.Combox_RotateTimes.SelectedIndex = 0;
            }
            if (this.Combox_CalibrationType.SelectedIndex == 2)
            {
                this.Combox_TranslationTimes.DataSource = new string[] { "3", "4", "5", "6", "7", "8", "9" };
                this.Combox_TranslationTimes.SelectedIndex = 0;
                this.Combox_RotateTimes.DataSource = new string[] { "3", "4", "5", "7", "8", "9", "10", "11", "12" };
                this.Combox_RotateTimes.SelectedIndex = 0;
            }
            CalibrationData = null;
        }

        private void button4_Click(object sender, EventArgs e)
        {
            try
            {
                if (PointPairs is null || PointPairs.Count == 0)
                {
                    this.TextBox_Log.Text = $"{TimeNow} 标定数据为空";
                }

                //平移标定(刚性变换-点点 刚性变换-点角 相似变换 仿射变换)
                if (this.Combox_CalibrationType.SelectedIndex == 0)
                {
                    double[] ImageXArr = PointPairs.Where(pp => pp.PointType == "平移").Select(pp => pp.ImageX).ToArray();
                    double[] ImageYArr = PointPairs.Where(pp => pp.PointType == "平移").Select(pp => pp.ImageY).ToArray();
                    double[] MachineXArr = PointPairs.Where(pp => pp.PointType == "平移").Select(pp => pp.MachineX).ToArray();
                    double[] MachineYArr = PointPairs.Where(pp => pp.PointType == "平移").Select(pp => pp.MachineY).ToArray();
                    double[] ImageAngleArr = PointPairs.Where(pp => pp.PointType == "平移").Select(pp => pp.ImageAngle).ToArray();
                    double[] MachineAngleArr = PointPairs.Where(pp => pp.PointType == "平移").Select(pp => pp.MachineAngle).ToArray();

                    //double RMSE = 0;
                    //HTuple HomMat2D = new HTuple();
                    //HTuple qx, qy;
                    //double[] QX, QY;

                    //if (this.Combox_CalibrationType.Text == "仿射变换")
                    //{
                    //    HOperatorSet.VectorToHomMat2d(ImageXArr, ImageYArr, MachineXArr, MachineYArr, out HomMat2D);
                    //    HOperatorSet.HomMat2dToAffinePar(HomMat2D, out HTuple sx, out HTuple sy, out HTuple phi, out HTuple theta, out HTuple tx, out HTuple ty);
                    //    CalibrationData = new CalibrationData();
                    //    CalibrationData.CalibrationDataType = "仿射变换";
                    //    CalibrationData.SX = sx;
                    //    CalibrationData.SY = sy;
                    //    CalibrationData.Phi = phi;
                    //    CalibrationData.Theta = theta;
                    //    CalibrationData.TX = tx;
                    //    CalibrationData.TY = ty;
                    //}

                    //验证误差 平方差
                    //HOperatorSet.AffineTransPoint2d(HomMat2D, ImageXArr, ImageYArr, out qx, out qy);
                    //QX = qx.DArr;
                    //QY = qy.DArr;
                    //RMSE = MyMathTool.RMSE2D(MachineXArr, MachineYArr, QX, QY);

                    //this.TextBox_Log.Text = TimeNow + "标定状态:标定成功" + Environment.NewLine + CalibrationData.ToString() + Environment.NewLine;
                    //this.TextBox_Log.Text += $"标定模块误差RMSE:{RMSE.ToString("F6")}" + Environment.NewLine;

                }
                //旋转标定
                else if (this.Combox_CalibrationType.SelectedIndex == 1)
                {
                    //旋转标定提供的点一定是连续的
                    double[] ImageXArr = PointPairs.Where(pp => pp.PointType == "旋转").Select(pp => pp.ImageX).ToArray();
                    double[] ImageYArr = PointPairs.Where(pp => pp.PointType == "旋转").Select(pp => pp.ImageY).ToArray();
                    double[] ImageAngleArr = PointPairs.Where(pp => pp.PointType == "旋转").Select(pp => pp.ImageAngle).ToArray();
                    double[] MachineAngleArr = PointPairs.Where(pp => pp.PointType == "旋转").Select(pp => pp.MachineAngle).ToArray();

                    HOperatorSet.GenContourPolygonXld(out HObject contour, ImageXArr, ImageYArr);//使用模板中心生成多边形XLD轮廓
                    HTuple row, column, radius, StartPhi, EndPhi, pointOrder;
                    HOperatorSet.FitCircleContourXld(contour, "algebraic", -1, 0, 0, 3, 2, out row, out column, out radius, out StartPhi, out EndPhi, out pointOrder);//拟合圆形
                    CalibrationData = new CalibrationData();
                    CalibrationData.CalibrationDataType = "旋转标定";
                    CalibrationData.ImageRotatoCenterX = column;
                    CalibrationData.ImageRotatoCenterY = row;
                    CalibrationData.ImageRotatoRadius = radius;

                    //验证误差 计算距离和实际距离的平方差
                    double[] Distances = new double[ImageYArr.Length];
                    for (int i = 0; i < Distances.Length; i++)
                    {
                        //Distances[i] = MyMathTool.Distance2Points(ImageYArr[i], ImageXArr[i], CalibrationData.ImageRotatoCenterX, CalibrationData.ImageRotatoCenterY);
                    }

                    double[] Radiuss = new double[ImageYArr.Length];
                    for (int i = 0; i < Radiuss.Length; i++)
                    {
                        Radiuss[i] = CalibrationData.ImageRotatoRadius;
                    }
                    //double RMSE = MyMathTool.RMSE1D(Radiuss, Distances);

                    //this.TextBox_Log.Text = TimeNow + "标定状态:标定成功" + Environment.NewLine + CalibrationData.ToString() + Environment.NewLine;
                    //this.TextBox_Log.Text += $"标定模块误差RMSE:{RMSE.ToString("F6")}" + Environment.NewLine;
                }
                else if (this.Combox_CalibrationType.SelectedIndex == 2)
                {
                    double[] ImageAngleArr = PointPairs.Where(pp => pp.PointType == "旋转").Select(pp => pp.ImageAngle).ToArray();
                    double[] MachineAngleArr = PointPairs.Where(pp => pp.PointType == "旋转").Select(pp => pp.MachineAngle).ToArray();
                    //MyMathTool.LinearFit(ImageAngleArr, MachineAngleArr, out double K, out double B);
                    //double LinearError = MyMathTool.LinearError(ImageAngleArr, MachineAngleArr);

                    //标定旋转中心
                    double[] ImageXRotatoArr = PointPairs.Where(pp => pp.PointType == "旋转").Select(pp => pp.ImageX).ToArray();
                    double[] ImageYRotatoArr = PointPairs.Where(pp => pp.PointType == "旋转").Select(pp => pp.ImageY).ToArray();
                    HOperatorSet.GenContourPolygonXld(out HObject contour, ImageYRotatoArr, ImageXRotatoArr);//使用模板中心生成多边形XLD轮廓
                    HTuple row, column, radius, StartPhi, EndPhi, pointOrder;
                    HOperatorSet.FitCircleContourXld(contour, "algebraic", -1, 0, 0, 3, 2, out row, out column, out radius, out StartPhi, out EndPhi, out pointOrder);//拟合圆形
                    CalibrationData = new CalibrationData();
                    CalibrationData.CalibrationDataType = "平移旋转标定";
                    double ImageRotatoCenterX = column;
                    double ImageYRotatoCenterY = row;
                    double ImageRotatoRadius = radius;

                    //确定旋转关键点
                    PointPair KeyPointPair = null;
                    bool ExistZeroPoint = PointPairs.Where(pp => pp.PointType == "旋转" && pp.ImageAngle == 0).Any();
                    if (ExistZeroPoint)
                    {
                        KeyPointPair = PointPairs.Where(pp => pp.PointType == "旋转" && pp.ImageAngle == 0).First();
                    }
                    else
                    {
                        KeyPointPair = PointPairs.Where(pp => pp.PointType == "旋转").First();
                    }

                    //根据关键点计算每个平移点的旋转中心位置
                    List<PointPair> PointPairsTrans = PointPairs.Where(pp => pp.PointType == "平移").ToList();
                    List<PointPair> PointPairsTransCenterList = PointPairsTrans.DeepClone();
                    foreach (var PointPairsTransCenter in PointPairsTransCenterList)
                    {
                        //string Result = MyMathTool.RotateCalculate(KeyPointPair.ImageX, KeyPointPair.ImageY, KeyPointPair.ImageAngle, ImageRotatoCenterX, ImageYRotatoCenterY, PointPairsTransCenter.ImageAngle, out double AngleRotato, out double XAfter, out double YAfter, out double XChange, out double YChange);
                        //if (Result != "OK")
                        //{
                        //    throw new Exception(Result);
                        //}
                        //double ImageXDiff = ImageRotatoCenterX - XAfter;
                        //double ImageYDiff = ImageYRotatoCenterY - YAfter;
                        //PointPairsTransCenter.ImageX = PointPairsTransCenter.ImageX + ImageXDiff;
                        //PointPairsTransCenter.ImageY = PointPairsTransCenter.ImageY + ImageYDiff;
                    }

                    //图像每个点的旋转中心和机械坐标做九点标定映射
                    double[] ImageXTransCenterArr = PointPairsTransCenterList.Where(pp => pp.PointType == "平移").Select(pp => pp.ImageX).ToArray();
                    double[] ImageYTransCenterArr = PointPairsTransCenterList.Where(pp => pp.PointType == "平移").Select(pp => pp.ImageY).ToArray();
                    double[] MachineXTransArr = PointPairs.Where(pp => pp.PointType == "平移").Select(pp => pp.MachineX).ToArray();
                    double[] MachineYTransArr = PointPairs.Where(pp => pp.PointType == "平移").Select(pp => pp.MachineY).ToArray();
                    HOperatorSet.VectorToHomMat2d(ImageXTransCenterArr, ImageYTransCenterArr, MachineXTransArr, MachineYTransArr, out HTuple HomMat2D);
                    HOperatorSet.HomMat2dToAffinePar(HomMat2D, out HTuple SX, out HTuple SY, out HTuple Phi, out HTuple Theta, out HTuple TX, out HTuple TY);

                    //综合标定RMSE误差计算
                    HTuple MachineXCalculate, MachineYCalculate;
                    List<double> ImageTransCenterXCalculate = new List<double>();
                    List<double> ImageTransCenterYCalculate = new List<double>();
                    foreach (var PointPair in PointPairs)
                    {
                        //string Result = MyMathTool.RotateCalculate(KeyPointPair.ImageX, KeyPointPair.ImageY, KeyPointPair.ImageAngle, ImageRotatoCenterX, ImageYRotatoCenterY, PointPair.ImageAngle, out double AngleRotatoCal, out double XAfterCal, out double YAfterCal, out double XChangeCal, out double YChangeCal);
                        //if (Result != "OK")
                        //{
                        //    throw new Exception(Result);
                        //}
                        //ImageTransCenterXCalculate.Add(PointPair.ImageX + (ImageRotatoCenterX - XAfterCal));
                        //ImageTransCenterYCalculate.Add(PointPair.ImageY + (ImageYRotatoCenterY - YAfterCal));
                    }
                    HOperatorSet.AffineTransPoint2d(HomMat2D, ImageTransCenterXCalculate.ToArray(), ImageTransCenterYCalculate.ToArray(), out MachineXCalculate, out MachineYCalculate);
                    double[] MachineXCalculateArr2 = MachineXCalculate.DArr;
                    double[] MachineYCalculateArr2 = MachineYCalculate.DArr;
                    //double RMSE = MyMathTool.RMSE2D(MachineXTransArr, MachineYTransArr, MachineXCalculateArr2, MachineYCalculateArr2);

                    ////测试计算一个点
                    //PointPair PointTest = new PointPair();
                    //PointTest.ImageX = 2137.16;
                    //PointTest.ImageY = 1291.99;
                    //PointTest.ImageAngle = -60;
                    ////MyMathTool.RotateCalculate(KeyPointPair.ImageX, KeyPointPair.ImageY, KeyPointPair.ImageAngle, ImageRotatoCenterX, ImageYRotatoCenterY, PointTest.ImageAngle, out double AngleRotatoCalTest, out double XAfterCalTest, out double YAfterCalTest, out double XChangeCalTest, out double YChangeCalTest);
                    //double ImageXTransFirstCenter = PointTest.ImageX + (ImageRotatoCenterX - XAfterCalTest);
                    //double ImageYTransFirstCenter = PointTest.ImageY + (ImageYRotatoCenterY - YAfterCalTest);
                    //HOperatorSet.AffineTransPoint2d(HomMat2D, ImageXTransFirstCenter, ImageYTransFirstCenter, out HTuple MachinXCal, out HTuple MachinYCal);
                  
                    ////生成标定结果
                    //CalibrationData.AngleLinearFitFactorK = K;
                    //CalibrationData.AngleLinearFitFactorB = B;
                    //CalibrationData.SX = SX;
                    //CalibrationData.SY = SY;
                    //CalibrationData.Phi = Phi;
                    //CalibrationData.Theta = Theta;
                    //CalibrationData.TX = TX;
                    //CalibrationData.TY = TY;
                    //CalibrationData.ImageRotatoCenterX = ImageRotatoCenterX;
                    //CalibrationData.ImageRotatoCenterY = ImageYRotatoCenterY;
                    //CalibrationData.ImageRotatoRadius = ImageRotatoRadius;
                    //CalibrationData.ImageKeyPointX = KeyPointPair.ImageX;
                    //CalibrationData.ImageKeyPointY = KeyPointPair.ImageY;
                    //CalibrationData.ImageKeyPointAngle = KeyPointPair.ImageAngle;

                    //this.TextBox_Log.Text = TimeNow + "标定状态:标定成功" + Environment.NewLine + CalibrationData.ToString() + Environment.NewLine;
                    //this.TextBox_Log.Text += $"圆角度线性拟合误差:{LinearError.ToString("F6")}" + Environment.NewLine;
                    //this.TextBox_Log.Text += $"综合标定模块误差RMSE:{RMSE.ToString("F6")}" + Environment.NewLine;

                }
            }
            catch (Exception ex)
            {
                this.TextBox_Log.Text = $"{TimeNow} 标定失败！！！";
                MessageBox.Show(ex.ToString());
            }
        }

        private void 导入标定数据ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (PointPairs is null || PointPairs.Count == 0)
            {
                MessageBox.Show("请先根据标定类型加载生成数据表格");
                return;
            }

            OpenFileDialog openFileDialog = new OpenFileDialog();
            openFileDialog.Multiselect = false;
            openFileDialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
            openFileDialog.Filter = "标定数据文件XML|*.xml";
            openFileDialog.Title = "导入标定数据文件XML";

            if (openFileDialog.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    List<PointPair> PointPairsFile = null;
                    using (FileStream fs = new FileStream(openFileDialog.FileName, FileMode.Open))
                    {
                        XmlSerializer xml = new XmlSerializer(typeof(List<PointPair>));
                        PointPairsFile = xml.Deserialize(fs) as List<PointPair>;
                        fs.Close();
                    }

                    if (PointPairsFile.Count != PointPairs.Count)
                    {
                        MessageBox.Show($"标定数据长度不兼容,当前导入标定数据文件长度为:{PointPairsFile.Count},请生成匹配的数据表格后重新导入");
                        return;
                    }
                    else
                    {
                        int TranslationFile = 0;
                        foreach (var PointPair in PointPairsFile)
                        {
                            if (PointPair.PointType == "平移")
                            {
                                TranslationFile++;
                            }
                        }
                        int Translation = 0;
                        foreach (var PointPair in PointPairs)
                        {
                            if (PointPair.PointType == "平移")
                            {
                                Translation++;
                            }
                        }
                        if (TranslationFile != Translation)
                        {
                            MessageBox.Show("标定数据类型不兼容 请检查生成匹配的数据表格后加载");
                            return;
                        }
                    }

                    PointPairs = PointPairsFile;

                    BindingList<PointPair> bs = new BindingList<PointPair>(PointPairs);
                    dataGridView1.DataSource = bs;
                    dataGridView1.Refresh();
                    ////CalibrationData = null;
                    this.TextBox_Log.Text = $"{TimeNow} 标定数据文件导入成功";
                }
                catch (Exception ex)
                {
                    CalibrationData = null;
                    this.TextBox_Log.Text = $"{TimeNow} 标定数据文件导入失败:{ex.Message}";
                }
            }
        }

        private void 另存标定文件ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (CalibrationData == null)
            {
                MessageBox.Show($"{TimeNow} 标定结果为空 导出标定结果失败");
                this.TextBox_Log.Text = $"{TimeNow} 标定结果为空 导出标定结果失败";
                return;
            }

            SaveFileDialog saveFileDialog = new SaveFileDialog();
            saveFileDialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
            saveFileDialog.Filter = "标定结果文件XML|*.xml";
            saveFileDialog.Title = "保存标定结果文件XML";
            saveFileDialog.AddExtension = true;

            if (saveFileDialog.ShowDialog() == DialogResult.OK)
            {
                if (File.Exists(saveFileDialog.FileName))
                {
                    File.Delete(saveFileDialog.FileName);
                }
                using (FileStream fs = new FileStream(saveFileDialog.FileName, FileMode.OpenOrCreate))
                {
                    XmlSerializer xml = new XmlSerializer(typeof(CalibrationData));
                    xml.Serialize(fs, CalibrationData);
                    fs.Close();
                }
                MessageBox.Show("标定结果文件保存成功");
            }
        }

        private void 另存标定数据ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (PointPairs is null || PointPairs.Count == 0)
            {
                MessageBox.Show($"{TimeNow} 标定数据为空 导出标定数据失败");
                this.TextBox_Log.Text = $"{TimeNow} 标定数据为空 导出标定数据失败";
            }

            SaveFileDialog saveFileDialog = new SaveFileDialog();
            saveFileDialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
            saveFileDialog.Filter = "标定数据文件XML|*.xml";
            saveFileDialog.Title = "保存标定数据文件XML";
            saveFileDialog.AddExtension = true;

            if (saveFileDialog.ShowDialog() == DialogResult.OK)
            {
                if (File.Exists(saveFileDialog.FileName))
                {
                    File.Delete(saveFileDialog.FileName);
                }
                using (FileStream fs = new FileStream(saveFileDialog.FileName, FileMode.OpenOrCreate))
                {
                    XmlSerializer xml = new XmlSerializer(typeof(List<PointPair>));
                    xml.Serialize(fs, PointPairs);
                    fs.Close();
                }
                this.TextBox_Log.Text = $"{TimeNow} 标定数据文件导出成功";
            }
        }


        /// <summary>
        /// 加载数据
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button2_Click(object sender, EventArgs e)
        {
            if (PointPairs is null || PointPairs.Count == 0)
            {
                MessageBox.Show("请先根据标定类型加载生成数据表格");
                return;
            }

            OpenFileDialog openFileDialog = new OpenFileDialog();
            openFileDialog.Multiselect = false;
            openFileDialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
            openFileDialog.Filter = "标定数据文件XML|*.xml";
            openFileDialog.Title = "导入标定数据文件XML";

            if (openFileDialog.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    List<PointPair> PointPairsFile = null;
                    using (FileStream fs = new FileStream(openFileDialog.FileName, FileMode.Open))
                    {
                        XmlSerializer xml = new XmlSerializer(typeof(List<PointPair>));
                        PointPairsFile = xml.Deserialize(fs) as List<PointPair>;
                        fs.Close();
                    }

                    if (PointPairsFile.Count != PointPairs.Count)
                    {
                        MessageBox.Show($"标定数据长度不兼容,当前导入标定数据文件长度为:{PointPairsFile.Count},请生成匹配的数据表格后重新导入");
                        return;
                    }
                    else
                    {
                        int TranslationFile = 0;
                        foreach (var PointPair in PointPairsFile)
                        {
                            if (PointPair.PointType == "平移")
                            {
                                TranslationFile++;
                            }
                        }
                        int Translation = 0;
                        foreach (var PointPair in PointPairs)
                        {
                            if (PointPair.PointType == "平移")
                            {
                                Translation++;
                            }
                        }
                        if (TranslationFile != Translation)
                        {
                            MessageBox.Show("标定数据类型不兼容 请检查生成匹配的数据表格后加载");
                            return;
                        }
                    }

                    PointPairs = PointPairsFile;

                    BindingList<PointPair> bs = new BindingList<PointPair>(PointPairs);
                    dataGridView1.DataSource = bs;
                    dataGridView1.Refresh();
                    ////CalibrationData = null;
                    this.TextBox_Log.Text = $"{TimeNow} 标定数据文件导入成功";
                }
                catch (Exception ex)
                {
                    CalibrationData = null;
                    this.TextBox_Log.Text = $"{TimeNow} 标定数据文件导入失败:{ex.Message}";
                }
            }
        }

        /// <summary>
        /// 保存数据
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button6_Click(object sender, EventArgs e)
        {
            if (PointPairs is null || PointPairs.Count == 0)
            {
                MessageBox.Show($"{TimeNow} 标定数据为空 导出标定数据失败");
                this.TextBox_Log.Text = $"{TimeNow} 标定数据为空 导出标定数据失败";
            }

            SaveFileDialog saveFileDialog = new SaveFileDialog();
            saveFileDialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
            saveFileDialog.Filter = "标定数据文件XML|*.xml";
            saveFileDialog.Title = "保存标定数据文件XML";
            saveFileDialog.AddExtension = true;

            if (saveFileDialog.ShowDialog() == DialogResult.OK)
            {
                if (File.Exists(saveFileDialog.FileName))
                {
                    File.Delete(saveFileDialog.FileName);
                }
                using (FileStream fs = new FileStream(saveFileDialog.FileName, FileMode.OpenOrCreate))
                {
                    XmlSerializer xml = new XmlSerializer(typeof(List<PointPair>));
                    xml.Serialize(fs, PointPairs);
                    fs.Close();
                }
                this.TextBox_Log.Text = $"{TimeNow} 标定数据文件导出成功";
            }
        }

        /// <summary>
        /// 保存调试参数
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button3_Click(object sender, EventArgs e)
        {
           
        }


        private void FormDownCameraCalibration_FormClosing(object sender, FormClosingEventArgs e)
        {
            FormSingle = null;  //标志单例被关闭，允许下次创建
        }

        private void textBox1_TextChanged(object sender, EventArgs e)
        {

        }

        private void button3_Click_1(object sender, EventArgs e)
        {
            if (CalibrationData == null)
            {
                MessageBox.Show($"{TimeNow} 标定结果为空 导出标定结果失败");
                this.TextBox_Log.Text = $"{TimeNow} 标定结果为空 导出标定结果失败";
                return;
            }

            SaveFileDialog saveFileDialog = new SaveFileDialog();
            saveFileDialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
            saveFileDialog.Filter = "标定结果文件XML|*.xml";
            saveFileDialog.Title = "保存标定结果文件XML";
            saveFileDialog.AddExtension = true;

            if (saveFileDialog.ShowDialog() == DialogResult.OK)
            {
                if (File.Exists(saveFileDialog.FileName))
                {
                    File.Delete(saveFileDialog.FileName);
                }
                using (FileStream fs = new FileStream(saveFileDialog.FileName, FileMode.OpenOrCreate))
                {
                    XmlSerializer xml = new XmlSerializer(typeof(CalibrationData));
                    xml.Serialize(fs, CalibrationData);
                    fs.Close();
                }
                MessageBox.Show("标定结果文件保存成功");
            }
        }
    }
}
